#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>

#define MAX 1024
#define MAX_SIZE 64 //最多支持输入 64 个指令

#define TOK_SIGNAL " "

//2023.3.18 更新
//新增 ls 命令时颜色高亮显示
//输入内建命令时，进行处理

////自动挡
//int getCom(char* argv[], char* s)
//{
//  //先将字符串分割：空格分割为 '\0'
//
//  assert(argv && s);
//
//  //首次分割
//  int pos = 0;
//
//  argv[pos++] = strtok(s, TOK_SIGNAL);
//  while(argv[pos++] = strtok(NULL, TOK_SIGNAL)); //连续分割
//
//  return 0;
//}
//

//手动挡
int getCom(char* argv[], char* s)
{
  assert(argv && s);

  int pos = 0;
  char* ps = s;
  while(*ps)
  {
    if(*ps == ' ')
    {
      *ps = '\0';
      ps++;
      argv[pos++] = s;
      s = ps;
    }
    else
      ps++;
  }

  argv[pos++] = s;  //需要注意最后一个
  argv[pos] = NULL; //确保安全性

  return 0;
}

void showEnv()
{
  extern char** environ;
  int i = 0;
  for(i = 0; environ[i]; printf("[%d]:%s\n", i, environ[i++]));
}

//模拟实现精简版 bash
int main(int argc, char* argv[], char* envp[])
{
  //打印提示符
  int Exit_code = 0;
  char myEnv[32][1024]; //暂存
  int env_pos = 0;  //下标
  while(1)
  {
    printf("[user@TestForBash DefDir]# "); //默认目录
    fflush(stdout);

    char command[MAX] = { 0 };
    char* argv[MAX_SIZE] = { 0 };
    char* s = fgets(command, sizeof(command), stdin);
    assert(s);  //防止读取失败
    (void)s;  //强制使用一次 s

    command[strlen(command) - 1] = '\0';  //设置 '\0'
    //printf("%s\n", command);
    
    //获取命令
    getCom(argv, command);

    //判断是否为 ls 命令
    if(strcmp(argv[0], "ls") == 0)
    {
      int pos = 1;
      while(argv[pos]) pos++;
      argv[pos++] = (char*)"--color=auto";  //自动添加高亮
      argv[pos] = NULL; //确保安全
    }

    //判断是否为内建命令
    if(strcmp(argv[0], "cd") == 0)
    {
      //确保不为空命令
      if(argv[1])
      {
        chdir(argv[1]);
      }

      continue;
    }
    else if(strcmp(argv[0], "export") == 0)
    {
      //添加环境变量
      if(argv[1])
      {
        strcpy(myEnv[env_pos], argv[1]);
        putenv(myEnv[env_pos++]);
      }
      continue;
    }
    else if(strcmp(argv[0], "env") == 0)
    {
      //查看环境变量表，此时查看的是当前进程的环境变量表
      showEnv();
      continue;
    }
    else if(strcmp(argv[0], "echo") == 0)
    {
      //查看环境变量
      const char* str = NULL;
      if(argv[1][0] == '$')
      {
        //查看退出码
        if(argv[1][1] == '?')
        {
          printf("%d\n", Exit_code);
          continue;
        }
        else
          str = getenv(argv[1] + 1);

        if(str)
          printf("%s:%s\n", argv[1] + 1, str);
      }
      continue;
    }

    pid_t id = fork();
    if(id == 0)
    {
      //子进程执行进程替换
      execvp(argv[0], argv);
      exit(2);  //进程替换失败
    }

    int status = 0;
    waitpid(id, &status, 0);  //等待子进程

    //子进程退出信息打印（错误/异常终止）
    if(WIFEXITED(status))
    {
      int exit_code = WEXITSTATUS(status);
      Exit_code= exit_code;

      //正常运行就不提示
      if(exit_code == 2)
        printf("process no found! exit_code:%d\n", exit_code);
    }
    else
      printf("process run fail! code dump:%d exit_signal:%d\n", (status & 0x7F), (status >> 7) & 1);
  }

  return 0;
}
